package com.motong.gongdan.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author ksim
 * @date 2022/9/28
 * @apiNote 合并对象
 */
@Slf4j
public class BeanMergeUtil {
    /**
     * 合并相同属性
     *
     * @param mergeRule 合并规则<属性名称, 冲突时合并规则>
     * @param first     第一个条件
     * @param second    第二个属性
     * @param dataClass 合并后存放的对象
     * @return 合并后的属性
     * @apiNote 合并规则   apposition(并存 准备后缀为bak的参数接收) first(前一个) last(后一个) min(数值小的,日期靠前的) max(数值大的,日期靠后的)
     * b                 deprecated (废弃 id 置为 -1 需要额外添加id属性名)   listIntersection(集合交集), stringCompareDeprecated (字符串比较不同废弃)
     */
    public static <T, B> B merge(Map<String, String> mergeRule, T first, T second, Class<B> dataClass) {
        try {
            B reData = dataClass.newInstance();
            if (Objects.isNull(second)) {
                return reData;
            }
            Field[] declaredFields = first.getClass().getDeclaredFields();
            Class<?> secondClass = second.getClass();
            AtomicBoolean stop = new AtomicBoolean(false);
            Arrays.stream(declaredFields).forEach(i -> {
                if (stop.get()) {
                    return;
                }
                try {
                    Field reDataFieldId = dataClass.getDeclaredField(Optional.ofNullable(mergeRule).orElse(Collections.emptyMap()).getOrDefault("modelId", "id"));
                    reDataFieldId.setAccessible(true);
                    i.setAccessible(true);
                    Object firstValue = i.get(first);
                    Field secondField = secondClass.getDeclaredField(i.getName());
                    secondField.setAccessible(true);
                    Object secondValue = secondField.get(second);
                    Field reDataField = dataClass.getDeclaredField(i.getName());
                    reDataField.setAccessible(true);

                    if (ObjectIsEmpty(firstValue) && ObjectIsEmpty(secondValue)) {
                        String rule = Optional.ofNullable(mergeRule).orElse(Collections.emptyMap()).getOrDefault(i.getName(), "first");
                        rule = firstValue.equals(secondValue) ? "first" : rule;
                        switch (rule) {
                            case "deprecated":
                                reDataFieldId.set(reData, Convert.convert(reDataFieldId.getType(), -1));
                                stop.set(true);
                                break;
                            case "stringCompareDeprecated":
                                try {
                                    if (firstValue.toString().equals(secondValue.toString())) {
                                        reDataField.set(reData, firstValue);
                                    } else {
                                        reDataFieldId.set(reData, Convert.convert(reDataFieldId.getType(), -1));
                                        stop.set(true);
                                    }

                                } catch (Exception e) {
                                    log.error("属性不是字符串!");
                                }
                                break;
                            case "apposition":
                                Field reDataFieldBak = dataClass.getDeclaredField(i.getName().concat("Bak"));
                                reDataFieldBak.setAccessible(true);
                                reDataField.set(reData, firstValue);
                                reDataFieldBak.set(reData, secondValue);
                                break;
                            case "first":
                                reDataField.set(reData, firstValue);
                                break;
                            case "last":
                                reDataField.set(reData, secondValue);
                                break;
                            case "min":
                                reDataField.set(reData, BeanMergeUtil.isMin(firstValue, secondValue, true));
                                break;
                            case "max":
                                reDataField.set(reData, BeanMergeUtil.isMin(firstValue, secondValue, false));
                                break;
                            case "listIntersection":
                                if (firstValue instanceof Collection || secondValue instanceof Collection) {
                                    Collection<?> intersection = CollectionUtil.intersection((Collection) firstValue, (Collection) secondValue);
                                    if (CollectionUtils.isEmpty(intersection)) {
                                        reDataFieldId.set(reData, Convert.convert(reDataFieldId.getType(), -1));
                                        stop.set(true);
                                        break;
                                    }
                                    reDataField.set(reData, intersection);
                                }
                                break;
                            default:
                                break;
                        }
                    } else if (ObjectIsEmpty(firstValue) || ObjectIsEmpty(secondValue)) {
                        reDataField.set(reData, ObjectIsEmpty(firstValue) ? firstValue : secondValue);
                    }
                } catch (Exception e) {
                    log.warn("指定属性不存在!");
                    //e.printStackTrace();
                }
            });
            return reData;
        } catch (Exception e) {
            log.error("获取整合实例失败!");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 比较获取值
     *
     * @param firstValue  第一个值
     * @param secondValue 第二个值
     * @param getMin      是否获取最小值
     * @return 最小/大值
     */
    public static <T> T isMin(T firstValue, T secondValue, boolean getMin) {
        boolean min = true;
        if (firstValue instanceof Date) {
            min = DateTime.of((Date) firstValue).isBefore((Date) secondValue);
        }
        if (firstValue instanceof Long) {
            min = (Long) firstValue < (Long) secondValue;
        }
        if (firstValue instanceof Integer) {
            min = (Integer) firstValue < (Integer) secondValue;
        }
        if (firstValue instanceof BigDecimal) {
            min = (((BigDecimal) firstValue).compareTo((BigDecimal) secondValue) < 0);
        }
        return (getMin == min) ? firstValue : secondValue;

    }

    /**
     * 判断数据是否为空
     *
     * @param value 第一个值
     * @return 是否为空
     */
    public static Boolean ObjectIsEmpty(Object value) {
        if (value instanceof Collection) {
            return !CollectionUtils.isEmpty((Collection<?>) value);
        } else {
            return Objects.nonNull(value);
        }
    }
}
